#!/usr/bin/env python
# -*- coding: utf8 -*-
# title       :
# description :
# author      :'ShenMeng'

import pymel.core as pmel
import maya.cmds as cmds
import maya.mel as mel
import maya.api.OpenMayaUI as omui
import yaml
import os
import re
import shutil
import sys

class Smplayblast(object):
    def __init__(self, config=None, data=None):
        self._data = data
        self._config = config
        self._pre_hooks = []
        self._post_hooks = []
        self._init_data()
        self._init_hooks()
        
    def _init_data(self):
        # TODO
        if self._data:
            return
        else:
            env_config = os.getenv('VERPLAYBLAST_CONFIG_PATH')
            if not env_config:
                #file_path = 'D:/develop/verplayblast/src/verplayblast/config/' + self._config
                file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config', self._config)
            else:
                file_path=os.path.join(env_config, self._config)
            self._data = self.parse_yaml_file(file_path.replace('\\', '/'))
            
    def _init_hooks(self):
        hooks_data = self._data.get('hooks')
        if hooks_data:
            temp_path = hooks_data.get('path', '')
            if not os.path.isdir(temp_path):
                #temp_path = 'D:/develop/verplayblast/src/verplayblast/'+'hooks'
                temp_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'hooks')
            hooks_path = temp_path.replace('\\', '/')
            if hooks_path not in sys.path:
                sys.path.append(hooks_path)
            pre_hooks = hooks_data.get('pre_hooks', '')
            post_hooks = hooks_data.get('post_hooks', '')
            if isinstance(pre_hooks, list):
                for hook in pre_hooks:
                    self._pre_hooks.append(self.get_handle(hook))
            if isinstance(post_hooks, list):
                for hook in post_hooks:
                    self._post_hooks.append(self.get_handle(hook))
                    
    @staticmethod
    def parse_yaml_file(file_path):
        data = None
        with open(str(file_path)) as f:
            data = yaml.load(f)
        return data
        
    @staticmethod
    def get_handle(name):
        try:
            del sys.modules[name]
        except:pass
        handle = __import__(name)
        return handle
        
    def get_file_name(self):
        scene_file_path = cmds.file(q=1, sn=1)
        scene_name = os.path.basename(scene_file_path)
        scene_folder = os.path.dirname(scene_file_path)
        file_name = os.path.splitext(scene_name)[0]
        if self._data['camera'].get('view', False) and self._data['camera'].get('regex', False):
            post_regex = self._data['camera'].get('post', False)
            if post_regex:
                if re.search(post_regex, file_name):
                    cam = self.get_camera()
                    file_name = '{}_{}'.format(file_name, cam)
                    return [scene_folder, file_name]
                
        return [scene_folder, file_name]
        
    @staticmethod
    def close_all_windows():
        all_panels=cmds.getPanel(all=1)
        for a_panel in all_panels:
            panelWin = a_panel + 'Window'
            if cmds.window(panelWin, q=1, ex=1):
                cmds.deleteUI(panelWin)
        if cmds.window('unifiedRenderGlobalsWindow', q=1, ex=1):
            if cmds.window('unifiedRenderGlobalsWindow', q=1, vis=1):
                cmds.window('unifiedRenderGlobalsWindow', e=1, vis=0)
                
    @staticmethod
    def get_attr_str(data):
        attr_str_list = []
        for key, value in data.iteritems():
            if isinstance(value, str):
                attr_str_list.append('{key}="{value}"'.format(key=key, value=value))

            else:
                attr_str_list.append('{key}={value}'.format(key=key, value=value))
        return ','.join(attr_str_list)
        
    @staticmethod
    def _get_current_cam():
        mview = omui.M3dView()
        active_view = mview.active3dView()
        cam_shape = camera_name = active_view.getCamera().fullPathName()
        cam_node = pmel.PyNode(cam_shape)
        return cam_node.getParent().name()
        
    def get_camera(self):
        if self._data['camera'].get('view', False):
            regex = self._data['camera'].get('regex', False)
            cam = self._get_current_cam()
            if regex:
                if re.match(str(regex), cam):
                    return str(cam)
                else:
                    raise ValueError("\ncamera name is not correct")
            
            return str(cam)
        else:
            cam = self._data['camera']['name']
            if not cmds.objExists(cam):
                raise ValueError("\nCan not find camera: %s" % cam)
            return str(cam)
        
    def get_camera_attr_data(self):
        return self._data['camera']['attrs']

    def get_display_data(self):
        return self._data['display']
        
    def get_options_data(self):
        return self._data['options']
              
    def set_cam_attr(self):
        cam = self.get_camera()
        cam_attr = self.get_camera_attr_data()
        for attr, value in cam_attr.iteritems():
            cmds.setAttr("{cam}.{attr}".format(cam=cam, attr=attr), value)
    
    def set_panel_display(self):
        #self.close_all_windows()
        cam = self.get_camera()
        cmds.modelEditor('modelPanel4', e=1, av=1)
        display_data = self.get_display_data()
        display_data['camera'] = str(cam)
        eval("""cmds.modelEditor('modelPanel4', e=1, %s)""" % self.get_attr_str(display_data))

    def get_frame_range(self):
        start_frame= int(cmds.playbackOptions(q=True, min=True))
        end_frame = int(cmds.playbackOptions(q=True, max=True))
        return [start_frame, end_frame]
        
    def get_width_height(self):
        if isinstance(self._data['outsize'], list):
            return self._data['outsize']
        else:
            width = cmds.getAttr('defaultResolution.width')
            height = cmds.getAttr('defaultResolution.height')
            return [width, height]
            
    def get_copy_path(self):
        copy_data = self._data['output'].get('copy')
        if copy_data:
            result = []
            _, file_name = self.get_file_name()
            mov_format = self.get_mov_fomat()
            copy_paths = self.get_path(copy_data)
            if isinstance(copy_paths, list):
                for file_folder in copy_paths:
                    #print file_folder
                    result.append(os.path.join(file_folder, file_name +mov_format).replace('\\', '/'))
            return result
            
    def get_path(self, data):
        _, file_name = self.get_file_name()
        if data.get('sub'):
            name_part = file_name.split('_')
            range_split = data['sub'].get('range')
            if isinstance(range_split, list):
                result_part = name_part[range_split[0]:range_split[1]]
            else:
                result_part = name_part
            if isinstance(data['path'], list):
                result = []
                for _path in data['path']:
                    # if os.path.isdir(_path):
                    #     result.append(os.path.join(_path, *result_part))
                    result.append(os.path.join(_path, *result_part))
                return result
            else:
                # if os.path.isdir(data['path']):
                #     return os.path.join(data['path'], *result_part)
                return os.path.join(data['path'], *result_part)
        return data['path']
        
    def get_mov_fomat(self):
        if self._data['options']['format'] == 'qt':
            mov_format = '.mov'
        else:
            mov_format = '.avi'
        return mov_format
        
    def get_output_path(self):
        main_data = self._data['output']['main']
        secen_folder, file_name = self.get_file_name()
        if main_data['samefolder']:
            file_folder = secen_folder
        else:
            file_folder = self.get_path(main_data)
            #if not os.path.isdir(file_folder) :
            #    return
        mov_format = self.get_mov_fomat()
        save_path = os.path.join(file_folder, file_name +mov_format)
        return save_path.replace('\\', '/')
        
    def do_copy(self, file_path):
        copy_paths = self.get_copy_path()
        if copy_paths:
            for _path in copy_paths:
                if os.path.isfile(_path):
                    os.remove(_path)
                if not os.path.isdir(os.path.dirname(_path)):
                    os.makedirs(os.path.dirname(_path))
                shutil.copy2(file_path.replace('\\', '/'), _path.replace('\\', '/'))
                
    def do_playblast(self, save_path):
        options_data = self.get_options_data()
        frame_range = self.get_frame_range()
        options_data['startTime'] = frame_range[0]
        options_data['endTime'] = frame_range[1]+1
        options_data['widthHeight'] = self.get_width_height()
        options_data['filename'] = str(save_path)
        cmds.refresh()
        src_file = eval("""cmds.playblast(clearCache=1,%s)""" % self.get_attr_str(options_data))
        return src_file

    def run_hooks(self, hooks):
        copy_paths = self.get_copy_path()
        save_path = self.get_output_path()
        frame_range = self.get_frame_range()
        if hooks:
            for hook_handle in hooks:
                hook_handle.main(copy_paths=copy_paths, save_path=save_path, frame_range=frame_range)

    def main(self):
        save_path = self.get_output_path()
        if not save_path:
            return
        self.set_panel_display()
        self.set_cam_attr()
        self.run_hooks(self._pre_hooks)
        src_file = self.do_playblast(save_path)
        self.do_copy(src_file)
        self.run_hooks(self._post_hooks)


if __name__ == "__main__":
    vp = Smplayblast(config='deformationcar.yml') 
    vp.main()